home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / UTILITIES / BASCAT / bascat / mdwopt.c < prev    next >
C/C++ Source or Header  |  1996-02-23  |  23KB  |  770 lines

  1. /*
  2.  * mdwopt.c
  3.  *
  4.  * Options parsing, similar to GNU getopt_long
  5.  *
  6.  * (c) 1996 Mark Wooding
  7.  */
  8.  
  9. /*----- Notices -----------------------------------------------------------*
  10.  *
  11.  * This program comes with no warranty, not even of any kind, unless
  12.  * someone other than the author offers to provide one.  It may be used
  13.  * and distributed under the terms of the GNU General Public Licence, in
  14.  * the interests of promoting freely available software for Linux.
  15.  */
  16.  
  17. /*----- External dependencies ---------------------------------------------*/
  18.  
  19. #include <ctype.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23.  
  24. #include "mdwopt.h"
  25.  
  26. /*----- Configuration things ----------------------------------------------*/
  27.  
  28. #ifdef __riscos
  29.   #define PATHSEP '.'
  30. #else
  31.   #define PATHSEP '/'
  32. #endif
  33.  
  34. /*----- Global variables --------------------------------------------------*/
  35.  
  36. mdwopt_data mdwopt_global={0,0,0,1,0, 0,0,0,0};
  37.  
  38. enum {
  39.   ord__permute=0,                  /* Permute the options (default) */
  40.   ord__return=1,                   /* Return non-option things */
  41.   ord__posix=2,                /* Do POSIX-type hacking */
  42.   ord__negate=4                /* Magic negate-next-thing flag */
  43. };
  44.  
  45. /*----- Main code ---------------------------------------------------------*/
  46.  
  47. /* --- mo__nextWord --- *
  48.  *
  49.  * Arguments:    int argc == number of command line options
  50.  *        char *argv[] == pointer to command line options
  51.  *        mdwopt_data *data == pointer to persistent state
  52.  *
  53.  * Returns:    Pointer to the next word to handle, or 0
  54.  *
  55.  * Use:        Extracts the next word from the command line or environment
  56.  *        variable.
  57.  */
  58.  
  59. static char *mo__nextWord(int argc,char * const *argv,mdwopt_data *data)
  60. {
  61.   if (data->ind==-1)
  62.   {
  63.     char *p=data->env;
  64.     char *q;
  65.     while (isspace(*p))
  66.       p++;
  67.     q=p;
  68.     while (*p && !isspace(*p))
  69.       p++;
  70.     *p=0;
  71.     data->env=p+1;
  72.     if (p!=q)
  73.       return (q);
  74.     free(data->estart);
  75.     data->env=0;
  76.     data->ind=1;
  77.   }
  78.  
  79.   if (data->next==argc)
  80.     return (0);
  81.   return (argv[data->next++]);
  82. }
  83.  
  84. /* --- mo__permute --- *
  85.  *
  86.  * Arguments:    char *argv[] == pointer to command line arguments
  87.  *        mdwopt_data *data == pointer to persistent data
  88.  *
  89.  * Returns:    --
  90.  *
  91.  * Use:        Moves a command line option into the right place.
  92.  */
  93.  
  94. static void mo__permute(char * const *argv,mdwopt_data *data)
  95. {
  96.   char **v=(char **)argv;
  97.   if (data->ind!=-1)
  98.   {
  99.     int i=data->next-1;
  100.     char *p=v[i];
  101.     while (i>data->ind)
  102.     {
  103.       v[i]=v[i-1];
  104.       i--;
  105.     }
  106.     v[i]=p;
  107.     data->ind++;
  108.   }
  109. }
  110.  
  111. /* --- mdwopt --- *
  112.  *
  113.  * Arguments:    int argc == number of command line arguments
  114.  *        char * const *argv == pointer to command line arguments
  115.  *        const char *shortopt == pointer to short options information
  116.  *        const struct option *longopts == pointer to long opts info
  117.  *        int *longind == where to store matched longopt
  118.  *        mdwopt_data *data == persistent state for the parser
  119.  *        int flags == various useful flags
  120.  *
  121.  * Returns:    Value of option found next, or an error character, or
  122.  *        EOF for the last thing.
  123.  *
  124.  * Use:        Reads options.  The routine should be more-or-less compatible
  125.  *        with standard getopts, although it provides many more
  126.  *        features even than the standard GNU implementation.
  127.  *
  128.  *        The precise manner of options parsing is determined by
  129.  *        various flag settings, which are described below.  By setting
  130.  *        flag values appropriately, you can achieve behaviour very
  131.  *        similar to most other getopt routines.
  132.  *
  133.  *
  134.  *        How options parsing appears to users
  135.  *
  136.  *        A command line consists of a number of `words' (which may
  137.  *        contain spaces, according to various shell quoting
  138.  *        conventions).  A word may be an option, an argument to an
  139.  *        option, or a non-option.  An option begins with a special
  140.  *        character, usually `-', although `+' is also used sometimes.
  141.  *        As special exceptions, the word containing only a `-' is
  142.  *        considered to be a non-option, since it usually represents
  143.  *        standard input or output as a filename, and the word
  144.  *        containing a double-dash `--' is used to mark all following
  145.  *        words as being non-options regardless of their initial
  146.  *        character.
  147.  *
  148.  *        Traditionally, all words after the first non-option have been
  149.  *        considered to be non-options automatically, so that options
  150.  *        must be specified before filenames.  However, this
  151.  *        implementation can extract all the options from the command
  152.  *        line regardless of their position.  This can usually be
  153.  *        disabled by setting one of the environment variables
  154.  *        `POSIXLY_CORRECT' or `_POSIX_OPTION_ORDER'.
  155.  *
  156.  *        There are two different styles of options: `short' and
  157.  *        `long'.
  158.  *
  159.  *        Short options are the sort which Unix has known for ages: an
  160.  *        option is a single letter, preceded by a `-'.  Short options
  161.  *        can be joined together to save space (and possibly to make
  162.  *        silly words): e.g., instead of giving options `-x -y', a user
  163.  *        could write `-xy'.  Some short options can have arguments,
  164.  *        which appear after the option letter, either immediately
  165.  *        following, or in the next `word' (so an option with an
  166.  *        argument could be written as `-o foo' or as `-ofoo').  Note
  167.  *        that options with optional arguments must be written in the
  168.  *        second style.
  169.  *
  170.  *        When a short option controls a flag setting, it is sometimes
  171.  *        possible to explicitly turn the flag off, as well as turning
  172.  *        it on, (usually to override default options).  This is
  173.  *        usually done by using a `+' instead of a `-' to introduce the
  174.  *        option.
  175.  *
  176.  *        Long options, as popularised by the GNU utilities, are given
  177.  *        long-ish memorable names, preceded by a double-dash `--'.
  178.  *        Since their names are more than a single character, long
  179.  *        options can't be combined in the same way as short options.
  180.  *        Arguments to long options may be given either in the same
  181.  *        `word', separated from the option name by an equals sign,
  182.  *        or in the following `word'.
  183.  *
  184.  *        Long option names can be abbreviated if necessary, as long
  185.  *        as the abbreviation is unique.  This means that options can
  186.  *        have sensible and memorable names but still not require much
  187.  *        typing from an experienced user.
  188.  *
  189.  *        Like short options, long options can control flag settings.
  190.  *        The options to manipulate these settings come in pairs: an
  191.  *        option of the form `--set-flag' might set the flag, while an
  192.  *        option of the form `--no-set-flag' might clear it.
  193.  *
  194.  *        It is usual for applications to provide both short and long
  195.  *        options with identical behaviour.  Some applications with
  196.  *        lots of options may only provide long options (although they
  197.  *        will often be only two or three characters long).  In this
  198.  *        case, long options can be preceded with a single `-'
  199.  *        character, and negated by a `+' character.
  200.  *
  201.  *        Finally, some (older) programs accept arguments of the form
  202.  *        `-<number>', to set some numerical parameter, typically a
  203.  *        line count of some kind.
  204.  *
  205.  *
  206.  *        How programs parse options
  207.  *
  208.  *        An application parses its options by calling mdwopt
  209.  *        repeatedly.  Each time it is called, mdwopt returns a value
  210.  *        describing the option just read, and stores information about
  211.  *        the option in a data block.  The value -1 is returned when
  212.  *        there are no more options to be read.  The `?' character is
  213.  *        returned when an error is encountered.
  214.  *
  215.  *        Before starting to parse options, the value data->ind must be
  216.  *        set to 0 or 1.  The value of data->err can also be set, to
  217.  *        choose whether errors are reported by mdwopt.
  218.  *
  219.  *        The program's `argc' and `argv' arguments are passed to the
  220.  *        options parser, so that it can read the command line.  A
  221.  *        flags word is also passed, allowing the program fine control
  222.  *        over parsing.  The flags are described above.
  223.  *
  224.  *        Short options are described by a string, which once upon a
  225.  *        time just contained the permitted option characters.  Now the
  226.  *        options string begins with a collection of flag characters,
  227.  *        and various flag characters can be put after options
  228.  *        characters to change their properties.
  229.  *
  230.  *        If the first character of the short options string is `+',
  231.  *        `-' or `!', the order in which options are read is modified,
  232.  *        as follows:
  233.  *
  234.  *        `+' forces the POSIX order to be used.  As soon as a non-
  235.  *            option is found, mdwopt returns -1.
  236.  *
  237.  *        `-' makes mdwopt treat non-options as being `special' sorts
  238.  *            of option.  When a non-option word is found, the value 0
  239.  *            is returned, and the actual text of the word is stored as
  240.  *            being the option's argument.
  241.  *
  242.  *        `!' forces the default order to be used.  The entire command
  243.  *            line is scanned for options, which are returned in order.
  244.  *            However, during this process, the options are moved in
  245.  *            the `argv' array, so that they appear before the non-
  246.  *            options.
  247.  *
  248.  *        A `:' character may be placed after the ordering flag (or at
  249.  *        the very beginning if no ordering flag is given) which
  250.  *        indicates that the character `:', rather than `?', should be
  251.  *        returned if a missing argument error is detected.
  252.  *
  253.  *        Each option in the string can be followed by a `+' sign,
  254.  *        indicating that it can be negated, a `:' sign indicating that
  255.  *        it requires an argument, or a `::' string, indicating an
  256.  *        optional argument.  Both `+' and `:' or `::' may be given,
  257.  *        although the `+' must come first.
  258.  *
  259.  *        If an option is found, the option character is returned to
  260.  *        the caller.  A pointer to an argument is stored in data->arg,
  261.  *        or NULL is stored if there was no argument.  If a negated
  262.  *        option was found, the option character is returned ORred with
  263.  *        gFlag_negated (bit 8 set).
  264.  *
  265.  *        Long options are described in a table.  Each entry in the
  266.  *        table is of type `struct option', and the table is terminated
  267.  *        by an entry whose `name' field is null.  Each option has
  268.  *        a flags word which, due to historical reasons, is called
  269.  *        `has_arg'.  This describes various properties of the option,
  270.  *        such as what sort of argument it takes, and whether it can
  271.  *        be negated.
  272.  *
  273.  *        When mdwopt finds a long option, it looks the name up in the
  274.  *        table.  The index of the matching entry is stored in the
  275.  *        `longind' variable, passed to mdwopt (unless `longind' is 0):
  276.  *        a value of -1 indicates that no long option was found.  The
  277.  *        behaviour is then dependent on the values in the table entry.
  278.  *        If `flag' is nonzero, it points to an integer to be modified
  279.  *        by mdwopt.  Usually the value in the `val' field is simply
  280.  *        stored in the `flag' variable.  If the flag gFlag_switch is
  281.  *        set, however, the value is combined with the existing value
  282.  *        of the flags using a bitwise OR.  If gFlag_negate is set,
  283.  *        then the flag bit will be cleared if a matching negated long
  284.  *        option is found.  The value 0 is returned.
  285.  *
  286.  *        If `flag' is zero, the value in `val' is returned by mdwopt,
  287.  *        possibly with bit 8 set if the option was negated.
  288.  *
  289.  *        Arguments for long options are stored in data->arg, as
  290.  *        before.
  291.  *
  292.  *        Numeric options, if enabled, cause the value `#' to be
  293.  *        returned, and the numeric value to be stored in data->opt.
  294.  *
  295.  *        If the flag gFlag_envVar is set on entry, options will be
  296.  *        extracted from an environment variable whose name is built by
  297.  *        capitalising all the letters of the program's name.  (This
  298.  *        allows a user to have different default settings for a
  299.  *        program, by calling it through different symbolic links.)
  300.  */
  301.  
  302. int mdwopt(int argc,char * const *argv,
  303.        const char *shortopt,
  304.        const struct option *longopts,int *longind,
  305.        mdwopt_data *data,int flags)
  306. {
  307.   /* --- Local variables --- */
  308.  
  309.   char *p,*q,*r;                   /* Some useful things to have */
  310.   char *prefix;                /* Prefix from this option */
  311.   int i;                /* Always useful */
  312.   char noarg='?';            /* Standard missing-arg char */
  313.  
  314.   /* --- Sort out our data --- */
  315.  
  316.   if (!data)                /* If default data requested */
  317.     data=&mdwopt_global;        /* Then use the global stuff */
  318.  
  319.   /* --- See if this is the first time --- */
  320.  
  321.   if (data->ind==0 || data->ind==1)    /* If data->ind has been zeroed */
  322.   {
  323.     /* --- Sort out default returning order --- */
  324.  
  325.     if (getenv("_POSIX_OPTION_ORDER") || /* Examine environment for opts */
  326.     getenv("POSIXLY_CORRECT"))    /* To see if we disable features */
  327.       data->order=ord__posix;        /* If set, use POSIX ordering */
  328.     else
  329.       data->order=ord__permute;        /* Otherwise mangle the options */
  330.  
  331.     /* --- Now see what the caller actually wants --- */
  332.  
  333.     switch (shortopt[0])        /* Look at the first character */
  334.     {
  335.       case '-':                /* `-' turns on reporting of names */
  336.         data->order=ord__return;
  337.     break;
  338.       case '+':                /* `+' turns on POSIXness */
  339.     data->order=ord__posix;
  340.     break;
  341.       case '!':                /* `!' ignores the POSIXness var */
  342.     data->order=ord__permute;    /* A [mdw] extension! */
  343.     break;
  344.     }
  345.  
  346.     /* --- Now decide on the program's name --- */
  347.  
  348.     p=q=(char *)argv[0];
  349.     while (*p)
  350.     {
  351.       if (*p++==PATHSEP)
  352.     q=p;
  353.     }
  354.     data->prog=q;
  355.  
  356.     data->ind=data->next=1;
  357.  
  358.     /* --- See about environment variables --- */
  359.  
  360.     if (flags & gFlag_envVar)        /* If we should parse env var */
  361.     {
  362.       char buf[32];
  363.  
  364.       p=buf;                /* Point to a buffer */
  365.       q=data->prog;            /* Point to program name */
  366.       while (*q)            /* While characters left here */
  367.     *p++=toupper(*q++);        /* Copy and uppercase */
  368.       *p++=0;                /* Terminate my copy of this */
  369.       p=getenv(buf);            /* Get the value of the variable */
  370.       if (p)                /* If it is defined */
  371.       {
  372.     q=malloc(strlen(p)+1);        /* Allocate space for a copy */
  373.     if (!q)                /* If that failed */
  374.     {
  375.       fprintf(stderr,        /* Report a nice error */
  376.           "%s: Not enough memory to read settings in "
  377.           "environment variable\n",
  378.           data->prog);
  379.     }
  380.     else                /* Otherwise */
  381.     {
  382.       strcpy(q,p);            /* Copy the text over */
  383.       data->ind=-1;            /* Mark that we're parsing envvar */
  384.       data->env=data->estart=q;    /* And store the pointer away */
  385.     }
  386.       }
  387.     }
  388.   }
  389.  
  390.   /* --- Do some initial bodgery --- *
  391.    *
  392.    * The shortopt string can have some interesting characters at the
  393.    * beginning.  We'll skip past them.
  394.    */
  395.  
  396.   switch (shortopt[0])
  397.   {
  398.     case '+':
  399.     case '-':
  400.     case '!':
  401.       shortopt++;
  402.       break;
  403.   }
  404.  
  405.   if (shortopt[0]==':')
  406.   {
  407.     noarg=shortopt[0];
  408.     shortopt++;
  409.   }
  410.  
  411.   if (longind)                /* Allow longind to be null */
  412.     *longind=-1;            /* Clear this to avoid confusion */
  413.   data->opt=-1;                /* And this too */
  414.   data->arg=0;                /* No option set up here */
  415.  
  416.   /* --- Now go off and search for an option --- */
  417.  
  418.   if (!data->list || !*data->list)
  419.   {
  420.     data->order &= 3;            /* Clear negation flag */
  421.  
  422.     /* --- Now we need to find the next option --- *
  423.      *
  424.      * Exactly how we do this depends on the settings of the order variable.
  425.      * We identify options as being things starting with `-', and which
  426.      * aren't equal to `-' or `--'.  We'll look for options until:
  427.      *
  428.      *   * We find something which isn't an option AND order==ord__posix
  429.      *   * We find a `--'
  430.      *   * We reach the end of the list
  431.      *
  432.      * There are some added little wrinkles, which we'll meet as we go.  */
  433.  
  434.     for (;;)                /* Keep looping for a while */
  435.     {
  436.       p=mo__nextWord(argc,argv,data);    /* Get the next word out */
  437.       if (!p)                /* If there's no next word */
  438.     return (EOF);            /* There's no more now */
  439.  
  440.       /* --- See if we've found an option --- */
  441.  
  442.       if ((p[0]=='-' || (p[0]=='+' && flags & gFlag_negation)) && p[1]!=0)
  443.       {
  444.     if (strcmp(p,"--")==0)        /* If this is the magic marker */
  445.       return (EOF);            /* There's nothing else to do */
  446.     break;                /* We've found something! */
  447.       }
  448.  
  449.       /* --- Figure out how to proceed --- */
  450.  
  451.       switch (data->order & 3)
  452.       {
  453.     case ord__posix:        /* POSIX option order */
  454.       return (EOF);            /* This is easy */
  455.       break;
  456.     case ord__permute:        /* Permute the option order */
  457.       break;
  458.     case ord__return:        /* Return each argument */
  459.       mo__permute(argv,data);    /* Insert word in same place */
  460.       data->arg=p;            /* Point to the argument */
  461.       return (0);            /* Return the value */
  462.       }
  463.     }
  464.  
  465.     /* --- We found an option --- */
  466.  
  467.     mo__permute(argv,data);        /* Do any permuting necessary */
  468.  
  469.     /* --- Check for a numeric option --- *
  470.      *
  471.      * We only check the first character (or the second if the first is a
  472.      * sign).  This ought to be enough.
  473.      */
  474.  
  475.     if (flags & gFlag_numbers)        /* If the caller set my flag */
  476.     {
  477.       if (((p[1]=='+' || p[1]=='-') && isdigit(p[2])) ||
  478.       isdigit(p[1]))
  479.       {
  480.     data->opt=atoi(p+1);
  481.     return ('#');
  482.       }
  483.     }
  484.  
  485.     /* --- Check for a long option --- */
  486.  
  487.     if (((p[0]=='-' && p[1]=='-') || (flags & gFlag_noShorts)) &&
  488.     (~flags & gFlag_noLongs))    /* Is this a long option? */
  489.     {
  490.       int match=-1;            /* Count matches as we go */
  491.  
  492.       if (p[0]=='+')            /* If it's negated */
  493.       {
  494.     data->order |= ord__negate;    /* Set the negate flag */
  495.     p++;                /* Point to the main text */
  496.     prefix="+";            /* Set the prefix string up */
  497.       }
  498.       else if (p[1]=='-')        /* If this is a `--' option */
  499.       {
  500.     if ((flags & gFlag_negation) && strncmp(p+2,"no-",3)==0)
  501.     {
  502.       p+=5;                /* Point to main text */
  503.       prefix="--no-";        /* And set the prefix */
  504.       data->order |= ord__negate;    /* Set the negatedness flag */
  505.     }
  506.     else
  507.     {
  508.       p+=2;                /* Point to the main text */
  509.       prefix="--";            /* Remember the prefix string */
  510.     }
  511.       }
  512.       else
  513.       {
  514.     if ((flags & gFlag_negation) && strncmp(p+1,"no-",3)==0)
  515.     {
  516.       p+=4;                /* Find the text */
  517.       prefix="-no-";        /* Set the prefix */
  518.       data->order |= ord__negate;    /* Set negatedness flag */
  519.     }
  520.     else
  521.     {
  522.       p++;                /* Otherwise find the text */
  523.       prefix="-";            /* And remember the prefix */
  524.     }
  525.       }
  526.  
  527.       for (i=0;longopts[i].name;i++)    /* Loop through the options */
  528.       {
  529.     if ((data->order & ord__negate) &&
  530.         (~longopts[i].has_arg & gFlag_negate))
  531.       continue;            /* If neg and opt doesn't allow */
  532.  
  533.     r=(char *)longopts[i].name;    /* Point to the name string */
  534.     q=p;                /* Point to the string start */
  535.     for (;;)            /* Do a loop here */
  536.     {
  537.       if (*q==0 || *q=='=')        /* End of the option string? */
  538.       {
  539.         if (*r==0)            /* If end of other string */
  540.         {
  541.           match=i;            /* This is the match */
  542.           goto botched;               /* And exit the loop now */
  543.         }
  544.         if (match==-1)        /* If no match currently */
  545.         {
  546.           match=i;            /* Then this is it, here */
  547.           break;            /* Stop looking now */
  548.         }
  549.         else
  550.         {
  551.           match=-1;            /* Else it's ambiguous */
  552.           goto botched;            /* So give up right now */
  553.         }
  554.       }
  555.       else if (*q!=*r)        /* Otherwise if mismatch */
  556.         break;            /* Abort this loop */
  557.       q++, r++;            /* Increment the counters */
  558.     }
  559.       }
  560.  
  561.     botched:
  562.       if (match==-1)            /* If we couldn't find a match */
  563.       {
  564.     if (data->err)
  565.     {
  566.       fprintf(stderr,"%s: unrecognised option `%s%s'\n",
  567.           data->prog,
  568.           prefix,p);
  569.     }
  570.     return ('?');
  571.       }
  572.  
  573.       if (longind)            /* Allow longind to be null */
  574.     *longind=match;            /* Store the match away */
  575.  
  576.       /* --- Handle argument behaviour --- */
  577.  
  578.       while (*p!=0 && *p!='=')        /* Find the argument string */
  579.     p++;
  580.       p=(*p ? p+1 : 0);            /* Sort out argument presence */
  581.       q=(char *)longopts[match].name;    /* Remember the name here */
  582.  
  583.       switch (longopts[match].has_arg & 3)
  584.       {
  585.     case no_argument:
  586.       if (p)
  587.       {
  588.         if (data->err)
  589.         {
  590.           fprintf(stderr,"%s: option `%s%s' does not accept arguments\n",
  591.               data->prog,
  592.               prefix,q);
  593.         }
  594.         return ('?');
  595.       }
  596.       break;
  597.  
  598.     case required_argument:
  599.       if (!p)            /* If no argument given */
  600.       {
  601.         p=mo__nextWord(argc,argv,data); /* Read the next argument out */
  602.  
  603.         if (!p)            /* If no more arguments */
  604.         {
  605.           if (data->err)
  606.           {
  607.         fprintf(stderr,"%s: option `%s%s' requires an argument\n",
  608.             data->prog,
  609.             prefix,q);
  610.           }
  611.           return (noarg);
  612.         }
  613.  
  614.         mo__permute(argv,data);
  615.       }
  616.       break;
  617.  
  618.     case optional_argument:
  619.       /* Who cares? */
  620.       break;
  621.       }
  622.       data->arg=p;
  623.  
  624.       /* --- Do correct things now we have a match --- */
  625.  
  626.       if (longopts[match].flag)        /* If he has a `flag' argument */
  627.       {
  628.         if (longopts[match].has_arg & gFlag_switch)
  629.     {
  630.       if (data->order & ord__negate)
  631.         *longopts[match].flag &= ~longopts[match].val;
  632.       else
  633.         *longopts[match].flag |= longopts[match].val;
  634.     }
  635.     else
  636.     {
  637.       if (data->order & ord__negate)
  638.         *longopts[match].flag = 0;
  639.       else
  640.         *longopts[match].flag = longopts[match].val;
  641.     }
  642.     return (0);            /* And return something */
  643.       }
  644.       else
  645.       {
  646.     if (data->order & ord__negate)
  647.       return (longopts[match].val | gFlag_negated);
  648.     else
  649.       return (longopts[match].val);
  650.       }
  651.     }
  652.  
  653.     /* --- Do short options things --- */
  654.  
  655.     else
  656.     {
  657.       if (p[0]=='+')            /* If starts with a `+' */
  658.     data->order |= ord__negate;
  659.       data->list=p+1;            /* Omit the leading `-'/`+' */
  660.     }
  661.   }
  662.  
  663.   /* --- Now process the short options --- */
  664.  
  665.   i=*data->list++;            /* Get the next option letter */
  666.   data->opt=i;                /* Store this away nicely */
  667.  
  668.   p=(char *)shortopt;                   /* Point to short opts table */
  669.   for (;;)
  670.   {
  671.     if (!*p)                /* No more options left */
  672.     {
  673.       if (data->err)
  674.       {
  675.     fprintf(stderr,"%s: unknown option `%c%c'\n",
  676.         data->prog,
  677.         data->order & ord__negate ? '+' : '-',
  678.         i);
  679.       }
  680.       return ('?');
  681.     }
  682.  
  683.     if (i!=*p || (p[1]!='+' && data->order & ord__negate))
  684.     {
  685.       p++;                /* Skip this option entry */
  686.       while (*p=='+')            /* Jump a `+' sign */
  687.     p++;
  688.       while (*p==':')            /* And jump any `:' characters */
  689.     p++;                /* Just in case there are any */
  690.     }
  691.     else
  692.       break;
  693.   }
  694.  
  695.   data->opt=i;                /* Store this for the caller */
  696.  
  697.   /* --- Sort out an argument, if we expect one --- */
  698.  
  699.   if (p[1]==':')            /* If we expect an option */
  700.   {
  701.     q=(data->list[0] ? data->list : 0);    /* If argument expected, use it */
  702.     data->list=0;            /* Kill the remaining options */
  703.     if (p[2]!=':' && !q)        /* If no arg, and not optional */
  704.     {
  705.       /* --- Same code as before --- */
  706.  
  707.       q=mo__nextWord(argc,argv,data);    /* Read the next word */
  708.       if (!q)                /* If no more arguments */
  709.       {
  710.     if (data->err)
  711.     {
  712.       fprintf(stderr,"%s: option `%c%c' requires an argument\n",
  713.           data->prog,
  714.           data->order & ord__negate ? '+' : '-',
  715.           i);
  716.     }
  717.     return (noarg);
  718.       }
  719.       mo__permute(argv,data);
  720.     }
  721.  
  722.     data->arg=q;
  723.   }
  724.   return ((data->order & ord__negate) ? i | gFlag_negated : i);
  725. }
  726.  
  727. /*----- Test driver -------------------------------------------------------*/
  728.  
  729. #ifdef TEST_MDWOPT
  730.  
  731. int main(int argc,char *argv[])
  732. {
  733.   int i;
  734.   static int flag=0;
  735.  
  736.   for (;;)
  737.   {
  738.     static struct option opt[]={
  739.       "help",0,0,'h',
  740.       "helpxy",0,0,'i',
  741.       "helpxz",0,0,'j',
  742.       "optarg",optional_argument,0,'o',
  743.       "reqarg",required_argument,0,'r',
  744.       "flaga",gFlag_negate,&flag,1,
  745.       "flagb",gFlag_negate,&flag,2,
  746.       "flagc",gFlag_negate,&flag,4,
  747.       0,0,0,0
  748.     };
  749.     i=mdwopt(argc,argv,"ho::r:f+g+h+",opt,0,0,
  750.          gFlag_numbers | gFlag_negation | gFlag_envVar | gFlag_noShorts);
  751.     if (i==-1)
  752.       break;
  753.     if (i=='#')
  754.       printf("Numeric option %i",optopt);
  755.     else
  756.       printf("Found option `%c'",i ? i : '!');
  757.     if (i & 256)
  758.       printf(" negated");
  759.     if (optarg)
  760.       printf(", arg==`%s'",optarg);
  761.     putc('\n',stdout);
  762.   }
  763.   while (optind<argc)
  764.     printf("Non-option argument `%s'\n",argv[optind++]);
  765.   printf("Flag value==%i\n",flag);
  766.   return (0);
  767. }
  768.  
  769. #endif
  770.